#ifndef UTIL_H_
#define UTIL_H_
#include <string>
#include <map>
#include <mutex>

#ifndef BASE64_H
#define BASE64_H

constexpr unsigned char from_b64(const unsigned char ch) {
    constexpr unsigned char tab[128] = {
            255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,
            255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,
            255,255,255,255,255,255,255,255,255,255,255,62,255,255,255,63,
            52,53,54,55,56,57,58,59,60,61,255,255,255,200,255,255,
            255,0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,
            15,16,17,18,19,20,21,22,23,24,25,255,255,255,255,255,
            255,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,
            41,42,43,44,45,46,47,48,49,50,51,255,255,255,255,255
    };
    return ch < 128 ? tab[ch] : 255;
}


static std::string base64_decode(const std::string &base64) {
    if (base64.empty()) { return "";}
    int len = static_cast<int>(base64.size());
    if (len % 4 != 0) { return "";}

    const auto *s = reinterpret_cast<const unsigned char*>(base64.data());
    unsigned char a, b, c, d;
    std::string out_data;

    auto end_s = s + base64.size();
    int count_eq = 0;
    while (*--end_s == '=') {
        count_eq++;
    }
    out_data.resize(len / 4 * 3 - count_eq);

    auto dst = out_data.data();
    while (len >= 4 && (a = from_b64(s[0])) != 255 &&
           (b = from_b64(s[1])) != 255 && (c = from_b64(s[2])) != 255 &&
           (d = from_b64(s[3])) != 255) {
        s += 4;
        len -= 4;
        if (a == 200 || b == 200) break; /* '=' can't be there */
        *dst++ = a << 2 | b >> 4;
        if (c == 200) break;
        *dst++ = b << 4 | c >> 2;
        if (d == 200) break;
        *dst++ = c << 6 | d;
    }
    return out_data;
}


static std::string base64_decode_char(const char *base64, const int str_len) {
    return base64_decode(std::string(base64, str_len));
}


static std::string base64_encode(const void *data, size_t size) {
    std::string encode_result;
    encode_result.reserve(size / 3 * 4 + (size % 3 != 0 ? 4 : 0));

    const auto *current = static_cast<const unsigned char *>(data);
    static auto base64_table = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
    while (size > 2) {
        encode_result += base64_table[current[0] >> 2];
        encode_result += base64_table[((current[0] & 0x03) << 4) + (current[1] >> 4)];
        encode_result += base64_table[((current[1] & 0x0f) << 2) + (current[2] >> 6)];
        encode_result += base64_table[current[2] & 0x3f];

        current += 3;
        size -= 3;
    }

    if (size > 0) {
        encode_result += base64_table[current[0] >> 2];
        if (size % 3 == 1) {
            encode_result += base64_table[(current[0] & 0x03) << 4];
            encode_result += "==";
        } else if (size % 3 == 2) {
            encode_result += base64_table[((current[0] & 0x03) << 4) + (current[1] >> 4)];
            encode_result += base64_table[(current[1] & 0x0f) << 2];
            encode_result += "=";
        }
    }
    return encode_result;
}

#endif //BASE64_H


#ifndef UUID_H
#define UUID_H
#ifdef _WIN32
#include <Rpc.h>
#pragma comment(lib, "Rpcrt4.lib")
#else
#include <uuid/uuid.h>
#endif

static std::mutex uuidMutex;
static std::mutex Mutex_string2base64;


static std::string generateUUID() {
#ifdef _WIN32
    UUID uuid;
    UuidCreate(&uuid);
    RPC_CSTR str;
    UuidToStringA(&uuid, &str);
    std::string uuidStr = reinterpret_cast<char *>(str);
    RpcStringFreeA(&str);
#else
    uuid_t uuid;
    std::lock_guard lock(uuidMutex);
    uuid_generate(uuid);
    char uuidStr[37];
    uuid_unparse(uuid, uuidStr);
    uuidStr[14] = '4'; // Set the version to 4
    uuidStr[19] = '8'; // Set the variant to IETF
#endif
    std::string uuid_;
    for (const auto &i: uuidStr) {
        if ('-' != i) {uuid_ += i;}
    }
    return uuid_;
}

#endif //UUID_H
#endif //UTIL_H_